#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <imp/imp_common.h>
#include <imp/imp_log.h>
#include <imp/imp_algo.h>
#ifdef DMALLOC
#include <imp/dmalloc.h>
#endif
#define TAG		"ISave"

typedef struct {
	IMPRect		rect;
	int			mcnt;	/* 0:inv; 0-tracetriggerthresd:shake; > tracetriggerthresd:change */
	int			scaned;
} IMatchRect;

typedef struct {
	int	r;		/* input rect index */
	int	m;		/* match list rect index */
} RectIndex;

struct ISave {
	ISaveParam	param;
	int			rsltFilter;
	bool		rsltLast;
	IMatchRect	*mrect;
	int			mrectCnt;
	RectIndex	*rectIndex;
};

int IMP_ALGO_ISaveCreate(ISave **h, ISaveParam *param)
{
	if (h == NULL || param == NULL) {
		IMP_LOG_ERR(TAG, "%s invalide input param: h=%p, param=%p\n", __func__, h, param);
		goto err_inv_param;
	}

	*h = calloc(1, sizeof(ISave));
	if (*h == NULL) {
		IMP_LOG_ERR(TAG, "%s calloc ISave failed\n", __func__);
		goto err_calloc_handler;
	}
	memcpy(&((*h)->param), param, sizeof(ISaveParam));

	(*h)->mrect = calloc((*h)->param.maxRectCnt, sizeof(IMatchRect));
	if ((*h)->mrect == NULL) {
		IMP_LOG_ERR(TAG, "%s calloc mrect failed\n", __func__);
		goto err_calloc_mrect;
	}

	(*h)->rectIndex = calloc((*h)->param.maxRectCnt, sizeof(RectIndex));
	if ((*h)->rectIndex == NULL) {
		IMP_LOG_ERR(TAG, "%s calloc rectIndex failed\n", __func__);
		goto err_calloc_rectIndex;
	}

	return 0;

err_calloc_rectIndex:
	free((*h)->mrect);
err_calloc_mrect:
	free(*h);
	*h = NULL;
err_calloc_handler:
err_inv_param:
	return -1;
}

int IMP_ALGO_ISaveDestroy(ISave *h)
{
	if (h) {
		if (h->rectIndex) {
			free(h->rectIndex);
		}
		if (h->mrect) {
			free(h->mrect);
		}
		free(h);
	}
	return 0;
}

static int algo_scan_match_list(ISave *h, IMPRect *pARect, int cnt)
{
	int i = 0, r = 0, m = 0, status = -1;
	int changeCnt = 0, rgapidx = 0, mgapidx = 0, mingapidx = 0;

	cnt = cnt > h->param.maxRectCnt ? h->param.maxRectCnt : cnt;
	if ((cnt == 0) && (h->mrectCnt > 0)) {
		for (m = 0; m < h->mrectCnt; m++) {
			if (h->mrect[m].mcnt > 1) {
				h->mrect[m].mcnt--;
			}
			if (h->mrect[m].mcnt > h->param.traceTriggerThresd) {
				changeCnt++;
			}
		}
		if (changeCnt > 0) {
			return 1;
		} else {
			return 0;
		}
	}

	for (i = 0; i < h->mrectCnt; i++) {
		h->mrect[i].scaned = 0;
	}

	for (r = 0; r < cnt; r++) {
		for (m = 0; m < h->mrectCnt; m++) {
			if (h->mrect[m].scaned == 1) {
				continue;
			}
			status = IMP_ALGO_RectMatch(h->mrect[m].rect, pARect[r],
					h->param.posThresd, h->param.sizeThresd);
			if (status < 0) {	/* inv */
				if (m == h->mrectCnt - 1) {
					h->rectIndex[rgapidx].r = r;
					rgapidx++;
				}
				if (r == cnt - 1) {
					h->rectIndex[mgapidx].m = m;
					mgapidx++;
				}
			} else if (status == 0) { /* shake */
				h->mrect[m].rect = pARect[r];
				h->mrect[m].scaned = 1;
				break;
			} else if (status == 1) { /* change */
				h->mrect[m].rect = pARect[r];
				h->mrect[m].mcnt++;
				h->mrect[m].scaned = 1;
				if (h->mrect[m].mcnt > h->param.traceTriggerThresd) {
					changeCnt++;
				}
				break;
			}
		}
	}

	mingapidx = rgapidx <= mgapidx ? rgapidx : mgapidx;
	for (i = 0; i < mingapidx; i++) {
		h->mrect[h->rectIndex[i].m].rect = pARect[h->rectIndex[i].r];
	}
	if (i < rgapidx) {
		int ml = h->mrectCnt;
		for (i = mingapidx, ml = h->mrectCnt; (i < rgapidx) && (ml < h->param.maxRectCnt); i++, ml++) {
			h->mrect[ml].rect = pARect[h->rectIndex[i].r];
			h->mrect[ml].mcnt = 0;
		}
		h->mrectCnt = ml;
	}
	h->mrectCnt = h->mrectCnt > cnt ? h->mrectCnt : cnt;

	if (changeCnt > 0) {
		return 1;
	}
	return 0;
}

/* return -1: err; return 0: not trace; return 1: trace */
int IMP_ALGO_ISaveTraceCheck(ISave *h, bool result, IMPRect *pARect, int cnt)
{
	int rslt = 0, i = 0;
	if (h == NULL || pARect == NULL || cnt > h->param.maxRectCnt) {
		IMP_LOG_ERR(TAG, "%s invalide param,h=%p, pARect=%p, cnt=%d\n", __func__, h, pARect, cnt);
		goto err_inv_h_pARect;
	}

	cnt = result == true ? cnt : 0;
	cnt = cnt > h->param.maxRectCnt ? h->param.maxRectCnt : cnt;
	/* filter out small rect */
	if (result == true) {
		for (i = 0; i < cnt; i++) {
			if (IMP_ALGO_RectFilter(pARect[i], h->param.rectAreaThresd) > 0) {
				break;
			}
		}
		if (i >= cnt) {
			result = false;
		}
	}

	/* filter out fake result */
	rslt = IMP_ALGO_BoolFilter(&h->rsltFilter, result, h->param.boolValThresd);

	/* deal with varial condition */
	if ((h->rsltLast == false) && (rslt > 0)) { /* s->d */
		/* fill match list */
		for (i = 0; i < cnt; i++) {
			h->mrect[i].rect = pARect[i];
			h->mrect[i].mcnt = 0;
			h->mrect[i].scaned = 0;
		}
		h->mrectCnt = cnt;
		h->rsltLast = true;
		goto notrace_outpoint;
	} else if ((h->rsltLast == true) && (rslt < 0)) { /* d->s */
		h->mrectCnt = 0;
		h->rsltLast = false;
		goto notrace_outpoint;
	} else if ((h->rsltLast == false) && (rslt < 0)) { /* s->s */
		/* nothing to do */
		goto notrace_outpoint;
	} else if ((h->rsltLast == true) && (rslt > 0)) { /* d-d */
		/* update match list */
		if (algo_scan_match_list(h, pARect, cnt) > 0) {
			return 1;
		}
	}

notrace_outpoint:
	return 0;

err_inv_h_pARect:
	return -1;
}
